package com.hxzon.project;

import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.hxzon.util.OgnlUtil;

public class EHibernateDaoSupport<M> extends HibernateDaoSupport implements EDao<M> {
	@Resource(name = "sessionFactory")
	// 为父类HibernateDaoSupport注入sessionFactory的值
	public void setSuperSessionFactory(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	private final Class<M> modelClass;
	private final List<String> modelStringFields;
	private final String fromString;

	@SuppressWarnings("unchecked")
	public EHibernateDaoSupport() {
		modelClass = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		modelStringFields = OgnlUtil.getAllStringProperty(modelClass);
		fromString = " from " + modelClass.getName() + " as p ";
	}

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	public Object query(final String hql, final long first, final long size) {
		return super.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {

				logger.debug(hql);
				Query query = session.createQuery(hql);
				if (first > -1 && size > 0) {
					query.setFirstResult((int) first);
					query.setMaxResults((int) size);
				}
				return query.list();
			}
		});
	}

	public Object query(String hql) {
		return query(hql, -1, -1);
	}

	// save delete
	public void delete(M model) {
		if (model == null) {
			return;
		}
		super.getHibernateTemplate().delete(model);

	}

	public void delete(String id) {
		if (id == null || id.isEmpty()) {
			return;
		}
		delete(get(id));

	}

	public void deleteAll() {
		deleteAll(getAll(-1, -1));
	}

	public void deleteAll(Collection<M> models) {
		if (models == null || models.isEmpty()) {
			return;
		}
		super.getHibernateTemplate().deleteAll(models);
	}

	// save
	public void save(M model) {
		if (model == null) {
			return;
		}
		super.getHibernateTemplate().saveOrUpdate(model);
	}

	public void saveAll(Collection<M> models) {
		if (models == null || models.isEmpty()) {
			return;
		}
		super.getHibernateTemplate().saveOrUpdateAll(models);
	}

	public void add(M model) {
		if (model == null) {
			return;
		}
		super.getHibernateTemplate().save(model);
	}

	public void addAll(Collection<M> models) {
		if (models == null || models.isEmpty()) {
			return;
		}
		for (M model : models) {
			super.getHibernateTemplate().save(model);
		}
	}
	
	public void update(M model) {
		if (model == null) {
			return;
		}
		super.getHibernateTemplate().update(model);
	}

	public void updateAll(Collection<M> models) {
		if (models == null || models.isEmpty()) {
			return;
		}
		for (M model : models) {
			super.getHibernateTemplate().update(model);
		}
	}

	// get
	@SuppressWarnings("unchecked")
	public M get(String id) {
		if (id == null || id.isEmpty()) {
			return null;
		}
		return (M) super.getHibernateTemplate().get(modelClass, id);
	}

	public M get(String fieldName, Object fieldValue) {
		if (fieldName == null || fieldName.isEmpty() || fieldValue == null) {
			return null;
		}
		List<M> list = getList(fieldName, fieldValue, -1, -1);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	@SuppressWarnings("unchecked")
	private List<M> doGetList(final String whereAndOrderBy, final long first, final long size) {
		return super.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				StringBuilder sb = new StringBuilder(fromString);
				if (whereAndOrderBy != null && !whereAndOrderBy.isEmpty()) {
					sb.append(whereAndOrderBy);
				}
				if (sb.indexOf("order by") == -1 && !orderBy().isEmpty()) {
					sb.append(orderBy());
				}
				logger.debug(sb.toString());
				Query query = session.createQuery(sb.toString());
				if (first > -1 && size > 0) {
					query.setFirstResult((int) first);
					query.setMaxResults((int) size);
				}
				return (List<M>) query.list();
			}
		});
	}

	// all
	public List<M> getAll() {
		return doGetList(null, -1, -1);
	}

	public List<M> getAll(long first, long size) {
		return doGetList(null, first, size);
	}

	public List<M> getAll(long first, long size, String orderBy) {
		return doGetList(orderBy, first, size);
	}

	// list
	public List<M> getList(String whereAndOrderBy) {
		return doGetList(whereAndOrderBy, -1, -1);
	}

	public List<M> getList(String whereAndOrderBy, long first, long size) {
		return doGetList(whereAndOrderBy, first, size);
	}

	public List<M> getList(String fieldName, Object fieldValue, long first, long size) {
		if (fieldValue instanceof String) {
			fieldValue = "'" + OgnlUtil.escape((String) fieldValue) + "'";
		}
		return doGetList(" where p." + fieldName + " = " + fieldValue, first, size);
	}

	@SuppressWarnings("unchecked")
	public List<M> getLike(final M example, List<String> fields, final long first, final long size) {
		final List<String> fields2 = new ArrayList<String>();
		Object value = null;
		for (int i = fields.size() - 1; i >= 0; i--) {
			value = OgnlUtil.getValue(example, fields.get(i));
			if (value != null && value instanceof String && !((String) value).isEmpty()) {
				fields2.add(fields.get(i));
			}
		}
		return super.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				StringBuilder hql = new StringBuilder(fromString);
				appendWhereString(fields2, example, hql);

				logger.debug(hql.toString());
				Query query = session.createQuery(hql.toString());
				if (first > -1 && size > 0) {
					query.setFirstResult((int) first);// fixme
					query.setMaxResults((int) size);
				}
				return (List<M>) query.list();
			}
		});
	}

//	@SuppressWarnings("unchecked")
	public List<M> getLike(final M modelExample, final long first, final long size) {
		 return getLike(modelExample, modelStringFields, first, size);
//		return super.getHibernateTemplate().executeFind(new HibernateCallback() {
//			public Object doInHibernate(Session session) throws HibernateException, SQLException {
//
//				Criteria query = session.createCriteria(modelClass).add(
//						Example.create(modelExample).ignoreCase().enableLike(MatchMode.ANYWHERE).excludeZeroes());
//				if (first > -1 && size > 0) {
//					query.setFirstResult((int) first);// fixme
//					query.setMaxResults((int) size);
//				}
//				return (List<M>) query.list();
//			}
//		});
	}

	@SuppressWarnings("unchecked")
	public List<M> getLike(final String like, final List<String> fields, final long first, final long size) {
		return super.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				StringBuilder hql = new StringBuilder(fromString);
				appendWhereString(fields, like, hql);
				logger.debug(hql.toString());
				Query query = session.createQuery(hql.toString());
				if (first > -1 && size > 0) {
					query.setFirstResult((int) first);// fixme
					query.setMaxResults((int) size);
				}
				return (List<M>) query.list();
			}
		});
	}

	public List<M> getLike(String like, long first, long size) {
		return getLike(like, modelStringFields, first, size);
	}

	// count
	private long doCount(String where) {
		StringBuilder hql = new StringBuilder("select count(p.id) ");
		hql.append(fromString);
		if (where != null && !where.isEmpty()) {
			hql.append(where);
		}
		List list = super.getHibernateTemplate().find(hql.toString());
		return (Long) list.get(0);

	}

	public long countAll() {
		return doCount(null);
	}

	public long countList(String where) {
		return doCount(where);
	}

	public long countList(String fieldName, Object fieldValue) {
		if (fieldValue instanceof String) {
			fieldValue = "'" + OgnlUtil.escape((String) fieldValue) + "'";
		}
		return doCount(" where p." + fieldName + " = " + fieldValue);
	}

	public long countLike(final M example, final List<String> fields) {
		final List<String> fields2 = new ArrayList<String>();
		Object value = null;
		for (int i = fields.size() - 1; i >= 0; i--) {
			value = OgnlUtil.getValue(example, fields.get(i));
			if (value != null && value instanceof String && !((String) value).isEmpty()) {
				fields2.add(fields.get(i));
			}
		}
		StringBuilder hql = new StringBuilder("select count(p.id) ");
		hql.append(fromString);
		appendWhereString(fields2, example, hql);
		// logger.debug(hql.toString());
		List list = super.getHibernateTemplate().find(hql.toString());
		return (Long) list.get(0);
	}

	public long countLike(M example) {
		return countLike(example, modelStringFields);
	}

	public long countLike(String like, List<String> fields) {
		StringBuilder hql = new StringBuilder("select count(p.id) ");
		hql.append(fromString);
		appendWhereString(fields, like, hql);
		List list = super.getHibernateTemplate().find(hql.toString());
		return (Long) list.get(0);
	}

	public long countLike(String like) {
		return countLike(like, modelStringFields);
	}

	private String appendWhereString(List<String> fields, String like, StringBuilder hql) {
		like = OgnlUtil.escape(like);
		if (!fields.isEmpty()) {// fixme not return String
			hql.append(" where p.").append(fields.get(0)).append(" like '%").append(like).append("%'");
		}
		for (int i = 1; i < fields.size(); i++) {
			hql.append(" or p.").append(fields.get(i)).append(" like '%").append(like).append("%'");
		}
		return hql.toString();
	}

	private void appendWhereString(List<String> fields, M example, StringBuilder hql) {
		if (!fields.isEmpty()) {
			OgnlUtil.escapeStringProperty(example);
			hql.append(" where p.").append(fields.get(0)).append(" like '%").append(
					OgnlUtil.getStringValueOrEmpty(example, fields.get(0)).replace("'", "''")).append("%'");
		}
		for (int i = 1; i < fields.size(); i++) {
			hql.append(" and p.").append(fields.get(i)).append(" like '%").append(
					OgnlUtil.getStringValueOrEmpty(example, fields.get(i)).replace("'", "''")).append("%'");
		}
	}

	protected String orderBy() {
		return "";
	}

}
